package com.autumn.cloud.lock.impl;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import com.autumn.cloud.lock.LockUnitOfWorker;
import com.autumn.cloud.lock.LockUnitOfWorkerResult;
import com.autumn.cloud.lock.MemoryReentrantLock;
import com.autumn.cloud.lock.UnableLockTimeoutException;

/**
 * 
 * @author 老码农
 *
 *         2017-11-23 13:04:47
 */
public class MemoryReentrantLockImpl extends AbstractResourceLock implements MemoryReentrantLock {

	private static final Map<String, ReentrantLock> LOCK_MAP = new ConcurrentHashMap<String, ReentrantLock>();

	private ReentrantLock getLock(String resourceName) {
		return LOCK_MAP.computeIfAbsent(resourceName, k -> new ReentrantLock());
	}

	@Override
	public void lock(String resourceName) throws Exception {
		ReentrantLock rlock = getLock(resourceName);
		rlock.lock();
	}

	@Override
	public void lock(String resourceName, int timeout) throws UnableLockTimeoutException, Exception {
		ReentrantLock rlock = getLock(resourceName);
		boolean seccess = rlock.tryLock(timeout, TimeUnit.MILLISECONDS);
		if (!seccess) {
			throw throwUnableLockTimeoutException(resourceName, timeout);
		}
	}

	@Override
	public void lock(String resourceName, LockUnitOfWorker lockUnitOfWorker) throws Exception {
		ReentrantLock rlock = getLock(resourceName);
		try {
			rlock.lock();
			if (lockUnitOfWorker != null) {
				lockUnitOfWorker.execute();
			}
		} finally {
			unlock(rlock, resourceName);
		}
	}

	@Override
	public void lock(String resourceName, LockUnitOfWorker lockUnitOfWorker, int timeout)
			throws UnableLockTimeoutException, Exception {
		ReentrantLock rlock = getLock(resourceName);
		boolean seccess = rlock.tryLock(timeout, TimeUnit.MILLISECONDS);
		if (seccess) {
			try {
				if (lockUnitOfWorker != null) {
					lockUnitOfWorker.execute();
				}
			} finally {
				unlock(rlock, resourceName);
			}
		} else {
			throw throwUnableLockTimeoutException(resourceName, timeout);
		}
	}

	@Override
	public <TResult> TResult lock(String resourceName, LockUnitOfWorkerResult<TResult> lockUnitOfWorker)
			throws Exception {
		ReentrantLock rlock = getLock(resourceName);
		try {
			rlock.lock();
			if (lockUnitOfWorker != null) {
				return lockUnitOfWorker.executeResult();
			}
			return null;
		} finally {
			unlock(rlock, resourceName);
		}
	}

	@Override
	public <TResult> TResult lock(String resourceName, LockUnitOfWorkerResult<TResult> lockUnitOfWorker, int timeout)
			throws UnableLockTimeoutException, Exception {
		ReentrantLock rlock = getLock(resourceName);
		boolean seccess = rlock.tryLock(timeout, TimeUnit.MILLISECONDS);
		if (seccess) {
			try {
				if (lockUnitOfWorker != null) {
					return lockUnitOfWorker.executeResult();
				}
				return null;
			} finally {
				unlock(rlock, resourceName);
			}
		} else {
			throw throwUnableLockTimeoutException(resourceName, timeout);
		}
	}

	@Override
	public void unlock(String resourceName) throws Exception {
		ReentrantLock rlock = LOCK_MAP.get(resourceName);
		if (rlock != null) {
			unlock(rlock, resourceName);
		}
	}

	private void unlock(ReentrantLock rlock, String resourceName) {
		rlock.unlock();
		LOCK_MAP.remove(resourceName);	
	}
}
